
#region Imports

using System;
using System.Collections.Generic;
using System.Reflection;

using Justa.Data;

#endregion

namespace Justa.Reflection
{
    /// <summary>
    /// 
    /// </summary>
    /// 
    public class AttributeReflector
    {
        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="A"></typeparam>
        /// 
        /// <param name="entity"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T GetSingleAttribute<T>(Type entity) where T : Attribute
        {
            Type target = GetConcreteType(entity.GetMethods());
            Console.WriteLine("Found type: {0}, received type: {1}", target.Name, typeof(T).Name);
            object[] attributes = target.GetCustomAttributes(typeof(T), false);
            Console.WriteLine("found: {0}", attributes.Length);

            if (attributes.Length == 0)
            {
                return default(T);
            }

            return attributes[0] as T;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="type"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T GetSingleFieldAttribute<T>(Type entity) where T : Attribute
        {
            //Type target = GetConcreteType(entity.GetMethods());
            foreach (FieldInfo fieldInfo in entity.GetFields())
            {
                object[] attributes = fieldInfo.GetCustomAttributes(typeof(T), false);
                if (attributes.Length == 0)
                {
                    continue;
                }
                else
                {
                    Console.WriteLine("found something...");
                    return attributes[0] as T;
                }
            }

            return default(T);
        }

        public static T GetSinglePropertyAttribute<T>(Type entity) where T : Attribute
        {
            foreach (PropertyInfo fieldInfo in entity.GetProperties())
            {
                object[] attributes = fieldInfo.GetCustomAttributes(typeof(T), false);
                if (attributes.Length == 0)
                {
                    continue;
                }
                else
                {
                    Console.WriteLine("found something...");
                    return attributes[0] as T;
                }
            }

            return default(T);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="type"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T GetSingleMethodAttribute<T>(Type type) where T : Attribute
        {
            foreach (MethodInfo methodInfo in type.GetMethods())
            {
                object[] attributes = methodInfo.GetCustomAttributes(typeof(T), false);
                if (attributes.Length == 0)
                {
                    continue;
                }
                else
                {
                    return attributes[0] as T;
                }
            }

            return default(T);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="entity"></param>
        /// 
        /// <returns></returns>
        /// 
        public static T[] GetAttributes<T>(Type entity) where T : Attribute
        {
            Console.WriteLine("getting attributes for type: {0}.", entity.Name);
            Type target = GetConcreteType(entity.GetMethods());
            object[] attributes = target.GetCustomAttributes(typeof(T), false);
            if (attributes.Length == 0)
            {
                return new T[] { }; 
            }

            return attributes as T[];
        }

        public static T[] GetMethodAttributes<T>(Type type) where T : Attribute
        {
            List<T> result = new List<T>();

            foreach (MethodInfo methodInfo in type.GetMethods())
            {
                object[] attributes = methodInfo.GetCustomAttributes(typeof(T), false);
                if (attributes.Length == 0)
                {
                    continue;
                }
                else
                {
                    result.Add(attributes[0] as T);
                    // return attributes[] as T[];
                }
            }

            return result.ToArray();  //default(T);
        }

        public static IDictionary<string, ColumnType> GetEntityColumns<T>(Type type) where T : Attribute
        {
            List<T> result = new List<T>();
            IDictionary<string, ColumnType> results = new Dictionary<string, ColumnType>();

            foreach (PropertyInfo propertyInfo in type.GetProperties())
            {
                object[] attributes = propertyInfo.GetCustomAttributes(typeof(T), false);
                if (attributes.Length != 0)
                {
                    results.Add(propertyInfo.Name, new ColumnType(propertyInfo.Name, ((ColumnAttribute)attributes[0]).Name, ColumnTypeEnum.COLUMN));

                    //continue;
                }
            }

            return results; //.ToArray();  //default(T);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="type"></param>
        /// 
        /// <returns></returns>
        /// 
        public static List<MethodInfo> GetAnotatedMethods<T>(Type type) where T : Attribute
        {
            List<MethodInfo> results = new List<MethodInfo>();

            foreach (MethodInfo methodInfo in type.GetMethods())
            {
                object[] attributes = methodInfo.GetCustomAttributes(typeof(T), false);
                if (attributes.Length == 0)
                {
                    continue;
                }
                else
                {
                    results.Add(methodInfo);
                }
            }

            return results;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <typeparam name="T"></typeparam>
        /// 
        /// <param name="entityType"></param>
        /// 
        /// <returns></returns>
        /// 
        public static bool IsAnotated<T>(Type entityType) where T : Attribute
        {
            Type target = GetConcreteType(entityType.GetMethods());
            object[] attributes = target.GetCustomAttributes(entityType, false);

            return (attributes.Length == 0);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="methodInfos"></param>
        /// 
        /// <returns></returns>
        /// 
        public static Type GetConcreteType(MethodInfo[] methodInfos)
        {
            if (methodInfos.Length == 0)
            {
                return null;
            }

            Type result = methodInfos[0].DeclaringType;

            foreach (MethodInfo methodInfo in methodInfos)
            {
                Type target = methodInfo.DeclaringType;
                if (target.IsSubclassOf(result))
                {
                    result = target;
                }
            }
            return result;
        }
    }
}
